﻿using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint.WebControls;
using System.IO;
using Microsoft.SharePoint;
using System.Linq;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.SharePoint.Administration;
using System.Text;
using System.Web;

namespace Slam.FieldTypes
{
	public partial class SlamFieldEditor : UserControl, IFieldEditor
	{
		private string _defaultFieldControlLocation = "~/_controltemplates/";

		protected void Page_Load(object sender, EventArgs e)
		{

		}

		protected override void CreateChildControls()
		{
			base.CreateChildControls();

			ddlUserControls.AutoPostBack = true;
			ddlUserControls.SelectedIndexChanged += new EventHandler(ddlUserControls_SelectedIndexChanged);
		}

		#region IFieldEditor Members

		public bool DisplayAsNewSection
		{
			get { return true; }
		}

		public void InitializeWithField(SPField field)
		{
			SlamField slamField = field as SlamField;

			if (Request.QueryString["slamfieldlocation"] != null)
			{
				txtUserControlLocation.Text = Request.QueryString["slamfieldlocation"];
			}

			if (!Page.IsPostBack)
			{
				LoadControls(slamField == null ? "" : slamField.UserControl, null);

				if (slamField != null)
				{
					txtCustomProperties.Text = slamField.CustomProperties;
					LoadControls(slamField.UserControl, slamField.GetActions());
				}

			}

			if (Request.QueryString["slamfieldeditor"] != null)
			{
				string location = txtUserControlLocation.Text;
				if (!location.StartsWith("~/"))
				{
					location = @"~\" + txtUserControlLocation.Text;
				}

				IFieldEditor editControl = null;
				try
				{
					editControl = Page.LoadControl(location.TrimEnd('\\') + @"\" + Request.QueryString["slamfieldeditor"]) as IFieldEditor;
				}
				catch { }

				if (editControl != null)
				{
					placeCustomEditor.Controls.Add((Control)editControl);
					editControl.InitializeWithField(field);
				}
				else
				{
					ifcCustomProperties.Visible = true;
				}
			}
		}

		protected void lnkReloadControls_Click(object sender, EventArgs e)
		{
			LoadControls("", null);
		}

		protected void lnkReloadActions_Click(object sender, EventArgs e)
		{
			LoadControls("", null);
		}

		private void LoadControls(string currentUserControl, List<SlamFieldActionMethod> currentActions)
		{
			Dictionary<string, string> fieldTypeControls = new Dictionary<string, string>();
			List<SlamFieldActionMethod> listActionMethods = new List<SlamFieldActionMethod>();
			Type iSlamFieldActionType = typeof(ISlamFieldAction);
			
			foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
			{
				try
				{
					foreach (Type type in assembly.GetTypes())
					{
						if (!type.IsAbstract && type.IsSubclassOf(Type.GetType("Slam.FieldTypes.SlamFieldControl")))
						{
							try
							{
								SlamFieldControl control = Activator.CreateInstance(type) as SlamFieldControl;
								if (control.TemplateName != null && !fieldTypeControls.ContainsKey(control.TemplateName))
								{
									fieldTypeControls.Add(control.TemplateName, type.AssemblyQualifiedName);
								}
							}
							catch (Exception ex)
							{
								SPDiagnosticsService.Local.WriteTrace(1, new SPDiagnosticsCategory("Slam Field Editor", TraceSeverity.High, EventSeverity.Error), TraceSeverity.High, "Failed to load type: " + type.AssemblyQualifiedName + ", Exception: " + ex.ToString(), null);
							}
						}
						else if (iSlamFieldActionType.IsAssignableFrom(type))
						{
							var attribute = type.GetCustomAttributes(typeof(SlamFieldActionAttribute), true).FirstOrDefault() as SlamFieldActionAttribute;
							if (attribute != null)
							{
								foreach (MethodInfo method in iSlamFieldActionType.GetMethods())
								{
									SlamFieldActionMethod action = new SlamFieldActionMethod();
									action.DisplayName = attribute.DisplayName;
									action.Class = type.AssemblyQualifiedName;
									action.Method = method.Name;
									action.MethodDefinition = attribute.ActionType.ToString() + "; " + type.AssemblyQualifiedName + "; " + method.Name;
									action.ActionType = attribute.ActionType;
									action.ExecutionOrder = 0;
									action.Synchronous = attribute.Synchronous;

									listActionMethods.Add(action);
								}
							}
						}
					}
				}
				catch (ReflectionTypeLoadException ex)
				{
					var loaderMessages = new StringBuilder();
					foreach (var loaderMessage in ex.LoaderExceptions)
					{
						loaderMessages.AppendLine(loaderMessage.Message);
					}
					SPDiagnosticsService.Local.WriteTrace(0, new SPDiagnosticsCategory("Slam Field Editor", TraceSeverity.High, EventSeverity.Error), TraceSeverity.High, "Failed to load types from assembly: " + assembly.FullName + ", loader messages: " + loaderMessages.ToString(), null);
				}
				catch (FileNotFoundException ex)
				{
					SPDiagnosticsService.Local.WriteTrace(0, new SPDiagnosticsCategory("Slam Field Editor", TraceSeverity.High, EventSeverity.Error), TraceSeverity.High, "Failed to load assembly file: " + ex.ToString(), null);
				}
			}

			if (String.IsNullOrEmpty(txtUserControlLocation.Text))
			{
				txtUserControlLocation.Text = _defaultFieldControlLocation;
			}

			string physicalPath = Page.Request.MapPath(txtUserControlLocation.Text).TrimEnd('\\') + "\\";
			string[] files = null;
			try
			{
				files = Directory.GetFiles(physicalPath);
			}
			catch (Exception ex)
			{
				lblError.Text = String.Format("Could not open user control path: {0}", ex.ToString());
				return;
			}

			List<string> sortedFiles = new List<string>(files);
			sortedFiles.Sort();

			List<LoadedUserControl> controls = new List<LoadedUserControl>();

			foreach (string file in sortedFiles)
			{
				if (file.EndsWith(".ascx"))
				{
					string fileName = file.Substring(physicalPath.Length);
					string location = txtUserControlLocation.Text;
					if (!location.StartsWith("~/"))
					{
						location = @"~\" + txtUserControlLocation.Text;
					}
					try
					{
						Control loadedControl = Page.LoadControl(location.TrimEnd('\\') + @"\" + fileName);
						if ((loadedControl.Controls.Count > 0 && loadedControl.Controls[0] is RenderingTemplate) || loadedControl is IFieldEditor)
						{
							LoadedUserControl matchedControlForEditor = controls.Find(c => c.UserControl.Replace("Control.", "Editor.") == fileName);
							if (matchedControlForEditor != null)
							{
								matchedControlForEditor.Editor = fileName;
							}
							else if (loadedControl.Controls[0] is RenderingTemplate)
							{

								LoadedUserControl control = new LoadedUserControl();
								control.UserControl = fileName;
								control.AssemblyQualifiedName = fieldTypeControls[((RenderingTemplate)loadedControl.Controls[0]).ID];
								controls.Add(control);
							}
						}
					}
					catch { }
				}
			}

			ddlUserControls.Items.Clear();
			ddlUserControls.DataSource = controls;
			ddlUserControls.DataTextField = "UserControl";
			ddlUserControls.DataValueField = "AssemblyQualifiedName";
			ddlUserControls.DataBind();

			ddlUserControlEditors.Items.Clear();
			ddlUserControlEditors.DataSource = controls;
			ddlUserControlEditors.DataTextField = "UserControl";
			ddlUserControlEditors.DataValueField = "Editor";
			ddlUserControlEditors.DataBind();

			ddlUserControls.Items.Insert(0, new ListItem("-- Select Control --"));





			if (Request.QueryString["SlamFieldControl"] != null)
			{
				ListItem currentItem = ddlUserControls.Items.FindByText(Request.QueryString["SlamFieldControl"]);
				if (currentItem != null)
				{
					currentItem.Selected = true;

					ListItem currentEditor = ddlUserControlEditors.Items.FindByText(Request.QueryString["SlamFieldControl"]);
					if (currentEditor != null)
					{
						currentEditor.Selected = true;
					}
				}
			}
			else if (!String.IsNullOrEmpty(currentUserControl))
			{
				ListItem currentItem = ddlUserControls.Items.FindByValue(currentUserControl);
				if (currentItem != null)
				{
					currentItem.Selected = true;

					ListItem currentEditor = ddlUserControlEditors.Items.FindByText(currentItem.Text);
					if (currentEditor != null)
					{
						currentEditor.Selected = true;
						ddlUserControls_SelectedIndexChanged(null, null);
					}
				}
			}

			// Action Methods Controls
			SetActions(txtLoadActions, listActionMethods, SlamFieldActionType.Load);
			SetActions(txtPreSaveActions, listActionMethods, SlamFieldActionType.PreSave);
			SetActions(txtPostSaveActions, listActionMethods, SlamFieldActionType.PostSave);

			// Load Selected ActionsMethods
			if (currentActions != null)
			{
				SetActions(txtSelectedLoadActions, currentActions, SlamFieldActionType.Load);
				SetActions(txtSelectedPreSaveActions, currentActions, SlamFieldActionType.PreSave);
				SetActions(txtSelectedPostSaveActions, currentActions, SlamFieldActionType.PostSave);

			}



		}

		private void SetActions(TextBox textBox, List<SlamFieldActionMethod> actionsMethods, SlamFieldActionType actionType)
		{
			var loadActionMethods = from actionMethod in actionsMethods
									where actionMethod.ActionType == actionType
									orderby actionMethod.ExecutionOrder ascending
									select actionMethod;

			StringBuilder builder = new StringBuilder("");
			foreach (SlamFieldActionMethod action in loadActionMethods)
			{
				builder.AppendLine("[[" + action.DisplayName + "; " + action.MethodDefinition + "; " + action.Synchronous.ToString() + "]]");
			}

			textBox.Text = builder.ToString().Replace("/n", Environment.NewLine);

		}



		protected void ddlUserControls_SelectedIndexChanged(object sender, EventArgs e)
		{
			placeCustomEditor.Controls.Clear();
			ListItem editorItem = ddlUserControlEditors.Items.FindByText(ddlUserControls.SelectedItem.Text);
			if (editorItem != null && !String.IsNullOrEmpty(editorItem.Value))
			{
				string location = txtUserControlLocation.Text;
				if (!location.StartsWith("~/"))
				{
					location = @"~\" + txtUserControlLocation.Text;
				}

				Control editControl = null;
				try
				{
					editControl = Page.LoadControl(location.TrimEnd('\\') + @"\" + editorItem.Value);
				}
				catch { }

				if (editControl != null)
				{
					string url = SPContext.Current.Web.Url + Request.Url.AbsolutePath;
					var queryString = HttpUtility.ParseQueryString(Request.Url.Query);

					queryString.Set("slamfieldeditor", editorItem.Value);
					queryString.Set("SlamFieldControl", ddlUserControls.SelectedItem.Text);
					//queryString.Set("slamfieldlocation", location);

					Response.Redirect(url + "?" + queryString.ToString());
				}
			}
		}

		public void OnSaveChange(SPField field, bool isNewField)
		{
				SlamField slamField = field as SlamField;
				slamField.UserControl = ddlUserControls.SelectedItem.Value;
				slamField.CustomProperties = txtCustomProperties.Text;
				slamField.Actions = "<Actions />";
				slamField.SetRichTextMode(false, SPRichTextMode.FullHtml);

				// Save Load Actions
				SaveActions(this.txtSelectedLoadActions, slamField);
				// Save PreSave Actions
				SaveActions(this.txtSelectedPreSaveActions, slamField);
				// Save PostSave Actions
				SaveActions(this.txtSelectedPostSaveActions, slamField);

				if (placeCustomEditor.Controls.Count > 0)
				{
					((IFieldEditor)placeCustomEditor.Controls[0]).OnSaveChange(field, isNewField);
				}
		}

		internal void SaveActions(TextBox selectedActions, SlamField field)
		{
			StringReader reader = new StringReader(selectedActions.Text);

			string line;
			int executionOrder = Constants.DefaultActionExecutionOrder;

			while ((line = reader.ReadLine()) != null)
			{
				line = line.Replace("[[", "");
				line = line.Replace("]]", "");

				string[] action = line.Split(';');

				if (action.Count() == 5)
				{
					SlamFieldActionMethod actionMethod = new SlamFieldActionMethod();
					actionMethod.DisplayName = action[0];
					actionMethod.ActionType = (SlamFieldActionType)Enum.Parse(typeof(SlamFieldActionType), action[1]);
					actionMethod.Class = action[2].Trim();
					actionMethod.Method = action[3].Trim();
					actionMethod.ExecutionOrder = executionOrder;
					actionMethod.Synchronous = bool.Parse(action[4].Trim());

					field.SetActions(actionMethod);

					executionOrder++;

				}
				else
					throw new Exception("Some of the selected options were not saved");
			}

		}

		#endregion
	}

	internal class LoadedUserControl
	{
		public string UserControl { get; set; }
		public string Editor { get; set; }
		public string AssemblyQualifiedName { get; set; }
	}
}
